package hello.java.app2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;

import com.microsoft.signalr.HubConnection;
import com.microsoft.signalr.HubConnectionBuilder;
import com.microsoft.signalr.TypeReference;

import io.reactivex.rxjava3.core.Single;

public class WsClient {

    private static final Logger logger = LoggerFactory.getLogger(WsClient.class);

    private String url;
    private String username;
    private String pwd;
    private HubConnection hubConnection;

    public WsClient(String url, String username, String pwd) {
        this.url = url;
        this.username = username;
        this.pwd = pwd;
    }

    public void connect() {
        // step one: get access token
        String baseUrl = this.url;
        TokenResponse tokenResponse = new TokenResponse();
        try {
            tokenResponse = RequestTokenCore.requestTokenCore(baseUrl, this.username, this.pwd);
            // logger.info(tokenResponse);
        } catch (IOException e) {
            e.printStackTrace();
            WsWrapper.run(this.url, this.username, this.pwd, true);
            return;
        }

        final String access_token = tokenResponse.access_token;
        // step two: connect to the signalr server
        String hubUrl = baseUrl + "/edms/ws/messagehub";
        hubConnection = HubConnectionBuilder.create(hubUrl)
                .withAccessTokenProvider(Single.defer(() -> {
                    return Single.just(access_token);
                })).build();

        // watch the PhoneStateChanged callback
        Type phoneListType = (new TypeReference<List<Phone>>() {
        }).getType();

        hubConnection.onClosed(exception -> {
            logger.warn("hubConnection close" + exception.toString());
            WsWrapper.run(this.url, this.username, this.pwd, true);
        });

        hubConnection.on("PhoneStateChanged", (phones) -> {
            logger.info("phone state changed:");
            logger.info(phones.toString());
        }, phoneListType);

        // watch the QueueStateChanged callback
        Type queueListType = (new TypeReference<List<CallStatus>>() {
        }).getType();
        hubConnection.on("QueueStateChanged", (callStatus) -> {
            logger.info("queue state changed:");
            logger.info(callStatus.toString());
        }, queueListType);

        // watch the PhoneStateChanged callback
        hubConnection.on("RecordFileArose", (calledInfo) -> {
            logger.info("calledInfo arose:");
            logger.info(calledInfo.toString());
        }, CalledInfo.class);

        try {
            // This is a blocking call util connect to signalr server
            hubConnection.start().blockingAwait();

            // when connected we should invoke the login method
            Single<LoginResult> loginResult = hubConnection.invoke(LoginResult.class, "Login");
            loginResult.subscribe((result) -> {
                logger.info("login result:");
                logger.info(result.toString());
            });

            // request phonelines on your need
            Single<List<Phone>> requestPhoneLineTask = hubConnection.invoke(phoneListType, "RequestPhoneLines");
            requestPhoneLineTask.subscribe((result) -> {
                logger.info("request phone lines:");
                logger.info(result.toString());
            });
        } catch (Exception e) {
            logger.warn("first connect failed:" + e.toString());
            WsWrapper.run(this.url, this.username, this.pwd, true);
        }
    }

    public void disconnect() {
        try {
            hubConnection.close();
        } catch (Exception e) {
            logger.warn(e.toString());
        }
    }
}